package clases.constructiva;

import interfaces.Heuristica;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import clases.CantidadYAparicionesEnClausulas;
import clases.CasoDePrueba;
import clases.Helper;
import clases.ResultadoCasoDePruebaArrayBinarios;



public class HeuristicaConstructiva implements Heuristica {
	
	private CantidadYAparicionesEnClausulas[] verdaderos = null;
	private CantidadYAparicionesEnClausulas[] falsos = null;
	
	public HeuristicaConstructiva() {
	}
	
	public static void main(String[] args) throws Exception {
		HeuristicaConstructiva h = new HeuristicaConstructiva();
		h.doIt();
	}
	
	public void doIt() throws Exception {
		Helper.leerArchivo(this,"Tp3HeuristicaConstructiva.out");
	}

	public ResultadoCasoDePruebaArrayBinarios dameLaSolucionDelCaso(CasoDePrueba casoDePrueba) {
		Integer cantVariables = casoDePrueba.v;
		
		CasoDePrueba cas = new CasoDePrueba(casoDePrueba);
		List<Integer> variablesRes = new LinkedList<Integer>();
		Set<Integer> listaProhibida = new HashSet<Integer>(); 
		ArrayList<ArrayList<Integer>> clausulasNoVacias = removerClausulasVacias(cas);
		cas.clausulas = clausulasNoVacias;
		
		while( cantVariables > 0 && !cas.clausulas.isEmpty()) {
			contarAparaciones(cas,listaProhibida);
			Integer variableMax = buscarMax(); // Si es negativo, falsos, si es positivo, verdaderos
			
			if(variableMax == 0) {
				break;
			}
			
			cas = removerClausulas(cas, variableMax);
			listaProhibida.add(variableMax);
			
			if(variableMax > 0) {
			    variablesRes.add(variableMax);
			}
			
			cantVariables--;
		}
		return new ResultadoCasoDePruebaArrayBinarios(casoDePrueba.clausulas.size() - cas.clausulas.size(), hacerStringDeVariables	(casoDePrueba.v,variablesRes), buscarListaDeClausulasResueltas(casoDePrueba, cas));
	}

	private ArrayList<ArrayList<Integer>> removerClausulasVacias(CasoDePrueba cas) {
		ArrayList<ArrayList<Integer>>clausulasNoVacias = new ArrayList<ArrayList<Integer>>();
		
		for (int i = 0; i < cas.clausulas.size(); i++) {
			
			if(cas.clausulas.get(i).size()!=0){
				clausulasNoVacias.add(cas.clausulas.get(i));
			}
		}
		
		return clausulasNoVacias;
		
	}

	private int[] hacerStringDeVariables(Integer cantVariables, List<Integer> variablesRes) {
		int[] res = new int[cantVariables] ;
		
		for (Iterator<Integer> iterator = variablesRes.iterator(); iterator.hasNext();) {
			Integer variable = (Integer) iterator.next();
			res[variable-1] = 1;
		}
		
		return res;
	}

	private ArrayList<Integer> buscarListaDeClausulasResueltas(CasoDePrueba casoDePrueba, CasoDePrueba cas) {
		ArrayList<Integer> res = new ArrayList<Integer>();
		for (int i = 0; i < casoDePrueba.clausulas.size(); i++) {
			if(!cas.clausulas.contains(casoDePrueba.clausulas.get(i))){
				res.add(i+1);
			}
		}
		return res;
	}

	private CasoDePrueba removerClausulas(CasoDePrueba cas, Integer variableMax) {
		//Si variableMax es negativa, voy a los falsos, sino a los verdaderos
		CantidadYAparicionesEnClausulas[] actual = null;
		
		if(variableMax > 0){
			actual = verdaderos;
		} else {
			actual = falsos;
			variableMax = variableMax * -1;
		}

		//Busco las clausulas a sacar, porque si las saco en el momento, los indices se me descontrolan
		List<ArrayList<Integer>> clausulasASacar = new LinkedList<ArrayList<Integer>>();
		for (Integer posClausula : actual[variableMax-1].clausulas) {
			clausulasASacar.add(cas.clausulas.get(posClausula));
		}
		
		//Saco las clausualas a sacar, del caso de prueba
		for (ArrayList<Integer> clausula : clausulasASacar) {
			cas.clausulas.remove(clausula);
		}
		
		return cas;
	}

	private Integer buscarMax() {
		Integer max = 0;
		Integer cantApariciones = 0;
		for (int i = 0; i < verdaderos.length; i++) {
			if(verdaderos[i].cantidad > cantApariciones){
				max = i + 1;
				cantApariciones = verdaderos[i].cantidad; 
			}
		}
		
		for (int i = 0; i < falsos.length; i++) {
			if(falsos[i].cantidad > cantApariciones){
				max = (i + 1) * -1;
				cantApariciones = falsos[i].cantidad; 
			}
		}
		return max;
	}

	private void contarAparaciones(CasoDePrueba casoDePrueba, Set<Integer> listaProhibida) {
		verdaderos = new CantidadYAparicionesEnClausulas[casoDePrueba.v];
		falsos = new CantidadYAparicionesEnClausulas[casoDePrueba.v];
		
		for (int i = 0; i < verdaderos.length; i++) {
			verdaderos[i] = new CantidadYAparicionesEnClausulas();
			falsos[i] = new CantidadYAparicionesEnClausulas();
		}
		
		
		int j = 0;
		for (; j < casoDePrueba.clausulas.size() ; j++) {
			
			ArrayList<Integer> clausula = casoDePrueba.clausulas.get(j);
			for(int i = 0; i < clausula.size(); i++) {
				//clausula.get(i) devuelve La variable de la clausula en la posicion i
				if(!listaProhibida.contains(clausula.get(i)*-1)) {
					if(clausula.get(i) > 0) {
						CantidadYAparicionesEnClausulas cant = verdaderos[clausula.get(i)-1];
						cant.cantidad++;
						cant.clausulas.add(j);
						verdaderos[clausula.get(i)-1] = cant;
					} else {
						CantidadYAparicionesEnClausulas cant = falsos[(clausula.get(i) * -1) - 1];
						cant.cantidad++;
						cant.clausulas.add(j);
						falsos[(clausula.get(i) * -1) - 1] = cant;					
					}
				}
			}
			
		}
		
		
		
		
	}
}
